#include "config.h"

#include "ms2sim.h"

static LIST_HEAD(nsessions);
static ecm_uint32	n_nsessions;
static ecm_uint64	total_n_sent_bytes;
static ecm_uint64	total_n_nsessions;

typedef struct {
	ecm_uint64	ts;
	/** total IO bytes */
	ecm_uint32	n_io_size;
	struct list_head	list;
} network_session_t;

static network_session_t *
create_nsession(session_t *session)
{
	network_session_t	*nsession;

	nsession = ecm_malloc(sizeof(network_session_t));
	if (nsession == NULL)
		return NULL;

	nsession->n_io_size = session->bytes_buffered - session->offset_buff;
	nsession->ts = session->ts;

	session->offset_buff = 0;

	INIT_LIST_HEAD(&nsession->list);

	return nsession;
}

static void
free_nsession(network_session_t *nsession)
{
	ECMASSERT(n_nsessions > 0);

	list_del(&nsession->list);
	n_nsessions--;
	ecm_free(nsession);
}

ecm_uint64
probe_network_system(ecm_uint64 ts)
{
	network_session_t	*nsession;
	ecm_uint32	remain_usec;

	if (list_empty(&nsessions))
		return TS_MAX;

	nsession = list_entry(nsessions.next, network_session_t, list);
	ECMASSERT(nsession->ts <= ts);

	DBG(DBG_NETWORK, "probe: ts:%s", dbg_timestep(ts));

	remain_usec = (ecm_uint32)(nsession->n_io_size * (1000000.0 / network_BW));
	return (ts + remain_usec);
}

void
schedule_network_system(ecm_uint64 ts)
{
	network_session_t	*nsession;

	ECMASSERT(!list_empty(&nsessions));

	nsession = list_entry(nsessions.next, network_session_t, list);
	ECMASSERT(nsession->ts <= ts);
	ECMASSERT(nsession->ts + (ecm_uint32)(nsession->n_io_size * (1000000.0 / network_BW)) <= ts);

	DBG(DBG_NETWORK, "schedule: ts:%s", dbg_timestep(ts));

	if (is_stat_started)
		total_n_sent_bytes += nsession->n_io_size;

	free_nsession(nsession);

	if (!list_empty(&nsessions)) {
		/* adjust next nsession */

		nsession = list_entry(nsessions.next, network_session_t, list);
		nsession->ts = ts;
	}
}

ECMBOOL
invoke_network_IO(session_t *session)
{
	network_session_t	*nsession;

	ECMASSERT(list_empty(&session->list));

	DBG(DBG_NETWORK, "invoke: session:[%s]", dbg_session(session));

	nsession = create_nsession(session);
	if (nsession == NULL)
		return ECM_FALSE;

	list_add_tail(&nsession->list, &nsessions);
	n_nsessions++;

	return ECM_TRUE;
}

void
check_network_stats(void)
{
	total_n_nsessions += n_nsessions;
}

void
report_network_result(void)
{
	ecm_double	avg_network_usage;
	ecm_double	avg_n_network_session;

	avg_network_usage = (1.0 * total_n_sent_bytes) / stat_duration;
	avg_n_network_session = (1.0 * total_n_nsessions) / stat_duration;

	printlog("Avg # of Network I/O Session:%.3lg\n", avg_n_network_session);
	printlog("Avg Network Usage:           %s\n", msl_get_bandwidth_str(avg_network_usage));
}

ECMBOOL
init_network(void)
{
	return ECM_TRUE;
}
